CloudWatch Eventsを使ってAWSリソース変更時にメール通知する
CloudWatch Events について
CloudWatch Eventは2016年1月に発表された比較的新しいサービスで、AWSリソースの変更情報をタイムリーにストリーム伝達します。
CloudWatch Events はイベントソースとして AWS API に対応しています。
AWS のリソースを変更する
- AWS マネージメントコンソール
- AWS CLI
- AWS SDK
といった手段はいずれも API を呼び出しているだけのため、AWS API をイベントソースに指定すると、変更手段によらずAWSリソースを変更するイベント全般をフックすることができます。
Route 53 リソースと連携させてみる
AWSにはAmazon Route 53というDNSサービスがあります。
一般的にDNS設定を変更する頻度は多くないものの、オペミスなどにより誤って設定変更すると、サービスへの影響が甚大です。 更新した時には即座に通知(周知)したいこともあるかと思います。
そこで CloudWatch Events を活用して、Route 53 のリソース変更に対してイベント発火する方法を紹介します。
アーキテクチャー
イベントのトリガー
今回は、Route 53 に対する更新系 API 操作をトリガーにします。
イベントの発火先
トリガーの発火先として、今回は Amazon SNS を利用します。
発火先には Amazon SNS 以外にも
- AWS Lambda
- Amazon SQS
- Amazon Kinesis Stream
- Bullet-in ターゲット
などに対応しています。
発火先には複数のターゲットを指定できます。
やってみた
Route 53 の API 呼び出しと CloudWatch Events/SNS を連携させましょう。
以下の順で作業します。
- CloudTrail の有効化
- SNS トピックの作成
- CloudWatch Eventsの作成
重要な点として、 Route 53 は API のエンドポイントが N. Virginia(us-east-1) リージョンのため、CloudTrail や SNS の操作は同リージョンで行います。
1. CloudTrail の有効化
今回は API 呼び出しをトリガーにするため、API 呼び出しが記録されるように AWS CloudTrail を有効にします。 CloudWatch Events を利用する・しないにかかわらず、API のログを残しておかないと、障害発生時の問題解決が困難になります。
AWS CloudTrailが無効だった場合は、今回を機に有効にしましょう。
Route 53 は API のエンドポイントが N. Virginia リージョンのため、少なくとも同リージョンで Cloud Trail を有効にします。
2. SNS トピックの作成
イベントの通知先 SNS トピックを作成します。
今回はトピック名を「cloudwatch-events-test」としました。 このトピックに対して確認のしやすさを優先してプロトコル「Email-JSON」でサブスクライブします。
運用にあわせて、プロトコル/エンドポイントは変更してください。
プロトコル「Email-JSON」の場合は疎通確認のEmailが届きます。 メールに記載されたリンクをクリックして疎通確認します。
3. AWS CloudWatch Eventsの作成
最後にメインとなる AWS CloudWatch Events を作成します。
管理画面の CloudWatch から Events -> Rules と選択し、ルールの作成画面に遷移します。
イベントのインプットとして
- 種類は「AWS API call」
- Service name は「Route 53」
を選択します。
イベントの発火先ターゲットとしてとして
- 種類は「SNS topic」
- トピック名は先程作成した「cloudwatch-events-test」
- Configure Input は「Matched event」
を選択します。
ルールの定義が完了すると、次のようになります。
動作確認
それでは、Route 53 の更新系 API を呼び出してみましょう。
AWS マネージメントコンソールから A レコードを変更した時
route53 の管理画面から A レコードを変更します。
変更すると以下のようなメールが通知されます。
Message キーが肝なため、Message キーの値を見やすく加工したのが以下です。
{ "version": "0", "id": "47399003-f31a-49f0-b1a6-a5976673bb0b", "detail-type": "AWS API Call via CloudTrail", "source": "aws.route53", "account": "123456789012", "time": "2016-05-21T16:30:51Z", "region": "us-east-1", "resources": [], "detail": { "eventVersion": "1.04", "userIdentity": { "type": "IAMUser", "principalId": "DUMMY", "arn": "arn:aws:iam::123456789012:user/cm-jane.doe", "accountId": "123456789012", "accessKeyId": "DUMMY", "userName": "cm-jane.doe", "sessionContext": { "attributes": { "mfaAuthenticated": "true", "creationDate": "2016-05-21T05:46:38Z" } }, "invokedBy": "signin.amazonaws.com" }, "eventTime": "2016-05-21T16:30:51Z", "eventSource": "route53.amazonaws.com", "eventName": "ChangeResourceRecordSets", "awsRegion": "us-east-1", "sourceIPAddress": "1.2.3.4", "userAgent": "signin.amazonaws.com", "requestParameters": { "changeBatch": { "changes": [ { "action": "CREATE", "resourceRecordSet": { "type": "A", "name": "www.dummy.com.", "resourceRecords": [ { "value": "192.0.2.235" } ], "tTL": 300 } } ] }, "hostedZoneId": "Z27S25GZV7IGA9" }, "responseElements": { "changeInfo": { "submittedAt": "May 21, 2016 4:30:51 PM", "status": "PENDING", "id": "/change/C2ROIRXW3Y2VIN" } }, "additionalEventData": { "Note": "Do not use to reconstruct hosted zone" }, "requestID": "61f6368c-1f71-11e6-beec-215485e52783", "eventID": "812ced6d-4d54-4ba9-870e-2df73b494648", "eventType": "AwsApiCall", "apiVersion": "2013-04-01" } }
detail キーの以下のキー
- "eventSource": "route53.amazonaws.com",
- "eventName": "ChangeResourceRecordSets",
- "awsRegion": "us-east-1",
から Route 53 の ChangeResourceRecordSets
API が us-east-1 リージョンに向けて呼ばれたことがわかります。
API のリクエストパラメーターは "requestParameters" キーにあります。
"requestParameters": { "changeBatch": { "changes": [ { "action": "CREATE", "resourceRecordSet": { "type": "A", "name": "www.dummy.com.", "resourceRecords": [ { "value": "192.0.2.235" } ], "tTL": 300 } } ] }, "hostedZoneId": "Z27S25GZV7IGA9" },
detail -> userIdentity -> invokedBy
が "signin.amazonaws.com" であることから AWS マネージメントコンソール 経由で実行されたこともわかります。
CLI からヘルスチェックを追加した時
{ "Port": 80, "Type": "HTTP", "ResourcePath": "/foo", "FullyQualifiedDomainName": "www.example.com", "RequestInterval": 10, "FailureThreshold": 1 }
というような JSON ファイル(test.json)を用意して CLI から Route 53 の API を叩いてみます。
$ aws route53 create-health-check --caller-reference 2014-04-01-18:47 --health-check-config file://test.json A client error (HealthCheckAlreadyExists) occurred when calling the CreateHealthCheck operation: A different health check has already been created with the specified caller reference.
create-helth-check API はエラーが起きたので実際には更新されませんが、CloudWatch Events のイベントは発火します。
先ほどと同じく、SNS 経由で送信された Email-JSON 形式のメールから重要な Message キーのバリューを見やすく加工したのが以下です。
{ "version": "0", "id": "a6b7cea4-2c4f-4589-b29f-b6396c8c75bd", "detail-type": "AWS API Call via CloudTrail", "source": "aws.route53", "account": "123456789012", "time": "2016-05-21T16:44:25Z", "region": "us-east-1", "resources": [], "detail": { "eventVersion": "1.04", "userIdentity": { "type": "IAMUser", "principalId": "DUMMY", "arn": "arn:aws:iam::123456789012:user/cm-jane.doe", "accountId": "123456789012", "accessKeyId": "DUMMY", "userName": "cm-jane.doe" }, "eventTime": "2016-05-21T16:44:25Z", "eventSource": "route53.amazonaws.com", "eventName": "CreateHealthCheck", "awsRegion": "us-east-1", "sourceIPAddress": "1.2.3.4", "userAgent": "aws-cli/1.10.32 Python/2.7.11 Darwin/15.4.0 botocore/1.4.22", "errorCode": "HealthCheckAlreadyExists", "errorMessage": "A different health check has already been created with the specified caller reference.", "requestParameters": { "healthCheckConfig": { "fullyQualifiedDomainName": "www.example.com", "failureThreshold": 1, "requestInterval": 10, "resourcePath": "/foo", "port": 80, "type": "HTTP" }, "callerReference": "2014-04-01-18:47" }, "responseElements": null, "requestID": "47467ad5-1f73-11e6-a791-6756bc02631c", "eventID": "5518a3f5-70c8-4a95-8d6f-c6f076179c45", "eventType": "AwsApiCall", "apiVersion": "2013-04-01" } }
detail -> userAgent が AWS CLI のもの("aws-cli/1.10.32 Python/2.7.11 Darwin/15.4.0 botocore/1.4.22"
)なので CLI 経由で呼び出されたことがわかります。
detail -> errorCode/errorMessage には AWS サーバーの返したエラーメッセージが含まれています。
- "errorCode": "HealthCheckAlreadyExists",
- "errorMessage": "A different health check has already been created with the specified caller reference.",
注意点
CloudWatch Events も万能ではありません。
List/Get/Describe 系のリードオンリーAPIには対応していません。 あくまで更新系 API が呼び出された場合のみイベントが発火されます。
また、更新系 API に利用したリクエストパラメーターはイベントメッセージに含まれますが、リソースが更新前にどういう状態だったかについては、知るすべがありません。
まとめ
今回は CloudWatch Events を使い、API 更新操作時にメール通知する方法を紹介しました。
CloudWatch Events を活用して安心・安定したサービス運用を目指しましょう。